
public class Slime extends Monster {
	//#if IMGGROUP_12x12 || IMGGROUP_12x12xLimited
		/**
		 * "Speed" of monster mooving
	 	 */
		static final byte STEP_X = 6;
		/**
		 * acceleration of gravity
		 */
		static final byte STEP_Y = 6;
		/**
		 * The distance within monster "sees" Marv
		 */
		static final short DISTANCE_WITHIN_RANGE = 66;
	//#elif IMGGROUP_18x18
		/**
		 * "Speed" of monster mooving
		 */
		static final byte STEP_X = 12;
		/**
		 * acceleration of gravity
		 */
		static final byte STEP_Y = 10;
		/**
		 * The distance within monster "sees" Marv
		 */
		static final short DISTANCE_WITHIN_RANGE = 100;
	//#elif IMGGROUP_24x24 || IMGGROUP_24x24xLimited
		/**
		 * "Speed" of monster mooving
		 */
		static final byte STEP_X = 16;
		/**
		 * acceleration of gravity
		 */
		static final byte STEP_Y = 13;
		/**
		 * The distance within monster "sees" Marv
		 */
		static final short DISTANCE_WITHIN_RANGE = 133;
	//#elif IMGGROUP_36x36
		/**
		 * "Speed" of monster mooving
		 */
		static final byte STEP_X = 24;
		/**
		 * acceleration of gravity
		 */
		static final byte STEP_Y = 20;
		/**
		 * The distance within monster "sees" Marv
		 */
		static final short DISTANCE_WITHIN_RANGE = 200;
	//#endif
		/**
		 * The x coordinate of the left edge of platform
		 */
		short leftEdgeBoundary;
		/**
		 * The x coordinate of the right edge of platform
		 */
		short rightEdgeBoundary;
		/** 
		 * Current steps of moving of monster
		 */
		private byte stepX, stepY;
		/**
		 * The y coordinate of the fall place 
		 */
		private short fallPlacePosY;
		/**
		 * The offset x of the current frame of animation
		 */
		private short offsetX;

	/**
	 *
	 */
	public Slime(short x, short y, short leftEdgeBoundary, short rightEdgeBoundary) {
		super(x, y);

		this.leftEdgeBoundary = leftEdgeBoundary;
		this.rightEdgeBoundary = rightEdgeBoundary;

		int i;
		animations = new Animation[Constants.ANIMATION_SLIME.length];
		for (i = 0; i < animations.length; i++) {
			animations[i] = new Animation(Constants.ANIMATION_SLIME[i]);
		}
		currentAnimation = animations[Constants.ANIMATION_SLIME_MOVE_LEFT];
		mode = -1;
		changeState(STATE_PATROLING);
	}

	/**
	 *
	 */
	public void update() {
		if (isActive) {
			move();
			switch (currentState) {
				case STATE_PATROLING:
					if (mode == MODE_MOVE_LEFT && (posX + offsetX) <= leftEdgeBoundary) {
						goToRight();
					}
					else if (mode == MODE_MOVE_RIGHT && (posX + offsetX) >= rightEdgeBoundary) {
						goToLeft();
					}
					if (isMarvWithInRange()) {
						changeState(STATE_ATTACKING);
					}
				break;
            
				case STATE_ATTACKING:
					if (isMarvWithInRange()) {
						if (isMarvReachable()) {
							changeState(STATE_DROPING);
						}
						else if(Hero.m_nPosX < posX) {
							goToLeft();
                        }
                        else if(Hero.m_nPosX > posX) {
							goToRight();
						}
					}
					else {
						changeState(STATE_PATROLING);
					}
				break;
            
				case STATE_DROPING:
					if (posY == fallPlacePosY) {
						changeState(STATE_IDLE);
					}
				break;

				case STATE_DEAD:
				break;
			}
			checkIntersect();
		}
	}
   
	/**
     * Method implements calculation of movements. Here is processing current monsetr mode, processing 
     * moving from left edge of platform to the right one
     */
	private void move() {
		switch (currentState) {
			case STATE_DROPING:
				posY += stepY;
				if (posY > fallPlacePosY) {
					posY = fallPlacePosY;
				}
			break;

			default:
				fallPlacePosY = GameWorld.monsterCollision(posX, posY);
			break;
		}
		currentAnimation.Tick();
		offsetX = (short)(currentAnimation.getLayerProperty(0, Constants.LAYERPROPERTY_OFFSET_X) + (currentAnimation.GetCurFrmWidth() >> 1));
		if ((currentAnimation.nStateFlg & Animation.cnANIM_STATE_END_OF_CYCLE) == Animation.cnANIM_STATE_END_OF_CYCLE) {
			switch (currentState) {
				case STATE_PATROLING:
				case STATE_ATTACKING:
					if (mode == MODE_MOVE_LEFT || mode == MODE_MOVE_RIGHT) {
						posX += stepX;
					}
				break;

				case STATE_DROPING:
					if (currentAnimation.nAnimID == AnimID.AnimID_MonsterDropStartDrop) {
						stepY = STEP_Y;
						posX += offsetX;
						currentAnimation = animations[Constants.ANIMATION_SLIME_FALL_DOWN];
						currentAnimation.Reset();
					}
				break;

				case STATE_IDLE:
					isActive = false;
				break;

				case STATE_DEAD:
					isActive = false;
				break;
			}
		}
	}
	
	/**
	 * Method makes a monster moving to the left
	 */
	private void goToLeft() {
		stepX = -STEP_X;
		if (mode != MODE_MOVE_LEFT) {
			posX += offsetX;
			currentAnimation = animations[Constants.ANIMATION_SLIME_MOVE_LEFT];
			currentAnimation.Reset();
			mode = MODE_MOVE_LEFT;
		}
	}
	
	/**
	 * Method makes a monster moving to the right
	 */
	private void goToRight() {
		stepX = STEP_X;
		if (mode != MODE_MOVE_RIGHT) {
			posX += offsetX;
			currentAnimation = animations[Constants.ANIMATION_SLIME_MOVE_RIGHT];
			currentAnimation.Reset();
			mode = MODE_MOVE_RIGHT;
		}
	}
	
	/**
	 * Method process on exit state
	 * @param prevState the previous State
	 */
	public void onExitState(byte prevState) {
		switch (currentState) {
			case STATE_PATROLING:
				stepX = 0;
			break;
         
			case STATE_ATTACKING:
			break;
         
			case STATE_DROPING:
			break;

			case STATE_DEAD:
			break;
		}
	}
	
	/**
	 * Method process on enter state
	 * @param newState the new State
	 */
	public void onEnterState(byte newState) {
		switch (currentState) {
			case STATE_PATROLING:
				stepY = 0;
				if (posX - leftEdgeBoundary >= rightEdgeBoundary - posX) {
					goToLeft();
				}
				else {
					goToRight();
				}
			break;
         
			case STATE_ATTACKING:
			break;
         
			case STATE_DROPING:
				stepY = 0;
				stepX = 0;
				posX += offsetX;
				currentAnimation = animations[Constants.ANIMATION_SLIME_START_FALL_DOWN];
			break;

			case STATE_IDLE:
				currentAnimation = animations[Constants.ANIMATION_SLIME_FLOOR_TOUCH];
			break;

			case STATE_DEAD:
				stepX = 0;
				stepY = 0;
				currentAnimation = animations[Constants.ANIMATION_SLIME_DEATH];
				mode = MODE_DEATH;
			break;
		}
	}
	
	/**
	 * Checks if Slime can reach Marv
	 */
	private boolean isMarvWithInRange() {
		int marvPosX = Hero.m_nPosX;
		int marvPosY = Hero.m_nPosY - Hero.animationCurrent.GetCurFrmHeight();
		int screenTop = Hero.m_nPosY - (MTMCanvas.height >> 1);
		int screenBottom;
		if (screenTop < 0) {
			screenTop = 0;
			screenBottom = MTMCanvas.height;
		}
		else {
			screenBottom = screenTop + MTMCanvas.height;
			if (screenBottom >= GameWorld.gps_height) {
				screenBottom = GameWorld.gps_height - 1;
				screenTop = screenBottom - MTMCanvas.height;
			}
		}
		if (leftEdgeBoundary <= marvPosX && marvPosX <= rightEdgeBoundary && posY <= marvPosY && marvPosY <= (posY + DISTANCE_WITHIN_RANGE)) {
			return Hero.m_nDeathTicks == 0 && Hero.m_nKeybMode == Hero.nModeNormal && !(fallPlacePosY < marvPosY) && posY >= screenTop;
		}
		return false;
	}
	
	/**
	 * 
	 */
	private boolean isMarvCollided(int marvPosX) {
		int mLeftX   = marvPosX - (Hero.animationCurrent.GetCurFrmWidth() >> 1);
		int mRightX  = marvPosX + (Hero.animationCurrent.GetCurFrmWidth() >> 1);
		int sPosX = posX + offsetX;
		return sPosX <= mRightX && sPosX >= mLeftX;
	}
	
	/**
	 *
	 */
	private boolean isMarvReachable() {
		int ticks = (Hero.m_nPosY - (Hero.animationCurrent.GetCurFrmHeight() >> 1) - posY) / STEP_Y + 1;
		int marvPosX = Hero.m_nPosX + (Hero.m_nSpeedRight - Hero.m_nSpeedLeft) * ticks;
		return isMarvCollided(marvPosX);
	}

	/**
	 */
	public void checkIntersect() {
		if (currentState != STATE_DEAD) {
			int monsterLeft   = posX - (currentAnimation.GetCurFrmHeight() >> 1);
			int monsterTop    = posY - currentAnimation.GetCurFrmHeight();
			int monsterRight  = posX + (currentAnimation.GetCurFrmHeight() >> 1);
			int monsterBottom = posY;

			int marvLeft   = Hero.m_nPosX - (Hero.animationCurrent.GetCurFrmWidth() >> 1);
			int marvTop    = Hero.m_nPosY - Hero.animationCurrent.GetCurFrmHeight();
			int marvRight  = Hero.m_nPosX + (Hero.animationCurrent.GetCurFrmWidth() >> 1);
			int marvBottom = Hero.m_nPosY;

			boolean isIntersected = Hero.m_nDeathTicks == 0 && Hero.m_nImmortalTicks == 0 && marvLeft < monsterRight && marvRight > monsterLeft && marvTop < monsterBottom && marvBottom > monsterTop;
			isIntersected = isIntersected && (Hero.m_nKeybMode == Hero.nModeNormal);

			if (isIntersected) {
				if (Hero.m_blDynamite || (Hero.m_nPickAxe > 0)) {
					changeState(STATE_DEAD);
					Hero.monsterKilled(Constants.TYPE_SLIME);
				}
				else {
					Hero.ForceDeath(Hero.c_nAnimID_Death);
					isActive = false;
				}
			}
		}
	}
}